package com.example.tangjiangxu.zoomimagedemo;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

/**
 * Created by josan_tang on 3/22/17.
 * 支持缩放的图片控件
 */

public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener {
    private static final String TAG = "ZoomImageView";

    /**
     * 最大放大的倍数
     */
    private static final float SCALE_MAX = 4.0f;
    private static final float SCALE_MID = 2.0f;
    /**
     * 缩放的手势检测
     **/
    private ScaleGestureDetector mScaleGestureDetector;
    /**
     * 双击手势
     **/
    private GestureDetector mGestureDetector;
    /**
     * 初始状态图片的缩放比例，当图片宽或者高大于屏幕时，该值小于1
     **/
    private float initScale = 1.0f;
    /**
     * 用于存放矩阵的9个值
     **/
    private final float[] matrixValues = new float[9];
    /**
     * 矩阵
     **/
    private final Matrix mScaleMatrix = new Matrix();
    /**
     * 视图首次添加上来
     **/
    private boolean firstAttch = true;
    /**
     * 最近一次触摸点个数
     **/
    private int lastPointerCount;
    /**
     * 上一次触摸点的平均x
     **/
    private float mLastX;
    /**
     * 上一次触摸点的平均y
     **/
    private float mLastY;
    /**
     * 是否是推动行为
     **/
    private boolean isCanDrag;
    /**
     * 能否左右移动
     **/
    private boolean isCheckLeftAndRight;
    /**
     * 能否上下移动
     **/
    private boolean isCheckTopAndBottom;
    /**
     * 是否在自动缩放
     **/
    private boolean isAutoScale;

    public ZoomImageView(Context context) {
        this(context, null);
    }

    public ZoomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //用矩阵来绘制，动态缩小放大图片来显示
        setScaleType(ScaleType.MATRIX);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        mGestureDetector = new GestureDetector(context, mSimpleOnGestureListener);
        this.setOnTouchListener(this);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        //设置布局监听
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    /**
     * 获得当前当缩放比例
     */
    private final float getScale() {
        mScaleMatrix.getValues(matrixValues);
        return matrixValues[Matrix.MSCALE_X];
    }

    /**
     * 缩放事件的回调方法
     **/
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        //当前ImageView资源真实缩放比例
        float scale = getScale();
        //手势缩放因子，大于1则为放大； 小于1则为缩小
        float scaleFactor = detector.getScaleFactor();
        Log.e(TAG, "onScale: scale:" + scale + ",scaleFactor :" + scaleFactor);
        if (getDrawable() == null) {
            return true;
        }

        //缩放的范围控制，放大时，最多为4倍；缩小时，为初始值
        /*if ((scale > SCALE_MAX && scaleFactor > 1.0f)
                || (scale > initScale && scaleFactor < 1.0f)) {*/
        if (scale >= SCALE_MAX || scale <= initScale) {
            if (scaleFactor * scale < initScale) {
                scaleFactor = initScale / scale;
            }
            if (scaleFactor * scale > SCALE_MAX) {
                scaleFactor = SCALE_MAX / scale;
            }
        }
        //设置缩放比例
        mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
        checkBorderAndCenterWhenScale();
        setImageMatrix(mScaleMatrix);
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {

    }

    /**
     * 触摸事件的回调方法
     **/
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if (mGestureDetector.onTouchEvent(event)) {
            return true;
        }
        mScaleGestureDetector.onTouchEvent(event);

        float x = 0, y = 0;
        //触摸点个数
        final int pointerCount = event.getPointerCount();
        //得到触摸点平均x、y值
        for (int i = 0; i < pointerCount; i++) {
            x += event.getX(i);
            y += event.getY(i);
        }
        x /= pointerCount;
        y /= pointerCount;
        //防止手指改变，平均位置变化太大
        if (pointerCount != lastPointerCount) {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;
        }
        lastPointerCount = pointerCount;

        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                RectF rectF = getMatrixRectF();
                //当图片宽高大于空间宽高时，避免ViewPager拦截事件
                Log.e(TAG, "onTouch: rectF.width():" +rectF.width() + ",getWidth:" + getWidth());
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                float dx = x - mLastX;
                float dy = y - mLastY;
                if (!isCanDrag) {
                    if (Math.sqrt((dx * dx) + (dy * dy)) >= ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
                        isCanDrag = true;
                    }
                }
                if (isCanDrag) {
                    if (getDrawable() != null) {
                        //拖动到边界时，交给父容器处理
                        if (rectF.left == 0 && dx > 0) {
                            getParent().requestDisallowInterceptTouchEvent(false);
                        }
                        if (rectF.right == getWidth() && dx < 0) {
                            getParent().requestDisallowInterceptTouchEvent(false);
                        }
                        isCheckLeftAndRight = isCheckTopAndBottom = true;
                        //判断是否能上下左右移动
                        if (rectF.width() < getWidth()) {
                            dx = 0;
                            isCheckLeftAndRight = false;
                        }

                        if (rectF.height() < getHeight()) {
                            dy = 0;
                            isCheckTopAndBottom = false;
                        }
                        mScaleMatrix.postTranslate(dx, dy);
                        checkMatrixBounds();
                        setImageMatrix(mScaleMatrix);

                    }
                }
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                lastPointerCount = 0;
                break;

        }
        return true;
    }

    /**
     * 移动时，进行边界判断主要判断宽或高大于屏幕
     */
    private void checkMatrixBounds() {
        RectF rect = getMatrixRectF();

        float deltaX = 0, deltaY = 0;
        final float viewWidth = getWidth();
        final float viewHeight = getHeight();
        //Log.e(TAG, "checkMatrixBounds: rect.top:" + rect.top + ",isCheckTopAndBottom:" + isCheckTopAndBottom +",rect.bottom:" + rect.bottom + ",isCheckTopAndBottom:" + isCheckTopAndBottom );
        if (rect.top > 0 && isCheckTopAndBottom) {
            deltaY = -rect.top;
        }
        if (rect.bottom < viewHeight && isCheckTopAndBottom) {
            deltaY = viewHeight - rect.bottom;
        }
        //∂Log.e(TAG, "checkMatrixBounds: rect.left:" + rect.left + ",isCheckLeftAndRight:" + isCheckLeftAndRight +",rect.right:" + rect.right + ",isCheckLeftAndRight:" + isCheckLeftAndRight );
        if (rect.left > 0 && isCheckLeftAndRight) {
            deltaX = -rect.left;
        }
        if (rect.right < viewWidth && isCheckLeftAndRight) {
            deltaX = viewWidth - rect.right;
        }
        Log.e(TAG, "checkMatrixBounds: deltaX:" + deltaX + ", deltaY:" + deltaY);
        mScaleMatrix.postTranslate(deltaX, deltaY);
    }

    @Override
    public void onGlobalLayout() {
        Log.e(TAG, "onGlobalLayout: ");
        if (firstAttch) {
            Drawable d = getDrawable();
            if (d == null) {
                return;
            }
            //ImageView控件的宽高
            int width = getWidth();
            int height = getHeight();
            //ImageView显示图片的资源
            int dw = d.getIntrinsicWidth();
            int dh = d.getIntrinsicHeight();
            float scale = 1.0f;
            //如果图片的宽或者高大于屏幕，则缩放至屏幕的宽或者高
            if (dw > width && dh <= height) {
                scale = width * 1.0f / dw;
            } else if (dh > height && dw <= width) {
                scale = height * 1.0f / dh;
            } else if (dw > width && dh > height) {
                //如果宽和高都大于屏幕，则让其按比例适应屏幕大小
                scale = Math.min(dw * 1.0f / width, dh * 1.0f / height);
            } else if (dw < width && dh < height) {
                //宽高都小于图片,则放大图片，让图片适应屏幕
                scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
            }
            initScale = scale;
            Log.e(TAG, "onGlobalLayout: initScale" + initScale);
            //图片移动至屏幕中心
            mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
            mScaleMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
            setImageMatrix(mScaleMatrix);
            firstAttch = false;
        }
    }

    /**
     * 缩放时，进行图片显示范围的控制
     */
    private void checkBorderAndCenterWhenScale() {
        //图片范围信息
        RectF rect = getMatrixRectF();
        float deltaX = 0;
        float deltaY = 0;
        //ImageView控件宽高
        int width = getWidth();
        int height = getHeight();
        //如果图片比屏幕宽
        if (rect.width() >= width) {
            Log.e(TAG, "checkBorderAndCenterWhenScale: rect.width():" + rect.width() + ", width:" + width + ",rect.left:" + rect.left + ", rect.right:" + rect.right);
            if (rect.left > 0) {
                deltaX = -rect.left;
            }
            if (rect.right < width) {
                deltaX = width - rect.right;
            }
        }

        if (rect.height() >= height) {
            Log.e(TAG, "checkBorderAndCenterWhenScale: rect.height():" + rect.height() + ", height:" + height + ",rect.bottom:" + rect.bottom + ", rect.top:" + rect.top);
            if (rect.top > 0) {
                deltaY = -rect.top;
            }
            if (rect.bottom < height) {
                deltaY = height - rect.bottom;
            }
        }

        //如果宽或高少于ImageView宽高，则居中
        if (rect.width() < width) {
            deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
        }
        if (rect.height() < height) {
            deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
        }
        Log.e(TAG, "checkBorderAndCenterWhenScale: deltaX=" + deltaX + ",deltaY=" + deltaY);

        mScaleMatrix.postTranslate(deltaX, deltaY);
    }

    /**
     * 根据当前图片的Matrix获得图片的范围
     *
     * @return
     */
    private RectF getMatrixRectF() {
        Matrix matrix = mScaleMatrix;
        RectF rect = new RectF();
        Drawable d = getDrawable();
        if (null != d) {
            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rect);
        }
        return rect;
    }

    /**
     * 双击的监听方法
     */
    GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (isAutoScale) {
                return true;
            }
            float x = e.getX();
            float y = e.getY();
            if (getScale() < SCALE_MID) {
                ZoomImageView.this.postDelayed(new AutoScaleRunable(SCALE_MID, x, y), 16);
            }/* else if (getScale() >= SCALE_MID && getScale() < SCALE_MAX) {
                ZoomImageView.this.postDelayed(new AutoScaleRunable(SCALE_MAX, x, y), 16);
            } */else {
                ZoomImageView.this.postDelayed(new AutoScaleRunable(initScale, x, y), 16);
            }
            isAutoScale = true;
            return true;
        }
    };

    /**
     * 支持自动缩放功能
     */
    class AutoScaleRunable implements Runnable {

        private static final float BIGGER = 1.07f;
        private static final float SMALLER = 0.93f;
        private float mTargetScale;
        private float tmpScale;
        /**
         * 缩放中心坐标
         */
        private float x;
        private float y;

        public AutoScaleRunable(float targetScale, float x, float y) {
            this.mTargetScale = targetScale;
            this.x = x;
            this.y = y;
            if (getScale() < mTargetScale) {
                tmpScale = BIGGER;
            } else {
                tmpScale = SMALLER;
            }
        }

        @Override
        public void run() {
            mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);

            final float currentScale = getScale();
            //如果值在合法范围内，继续缩放
            if (((tmpScale > 1f) && (currentScale < mTargetScale))
                    || ((tmpScale < 1f) && (currentScale > mTargetScale))) {
                postDelayed(this, 16);
            } else {
                //设置为目标的缩放比例
                final float deltaScale = mTargetScale / currentScale;
                mScaleMatrix.postScale(deltaScale, deltaScale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
                isAutoScale = false;
            }
        }
    }
}
